Описание проекта
Инвесторы из фонда «Shut Up and Take My Money» решили попробовать себя в новой области и открыть заведение общественного питания в Москве. Цель проекта: подготовить исследование рынка Москвы, найти интересные особенности и презентовать полученные результаты, которые в будущем помогут в выборе подходящего инвесторам места.
Описание данных
Нам доступен датасет с заведениями общественного питания Москвы, составленный на основе данных сервисов Яндекс Карты и Яндекс Бизнес на лето 2022 года. Информация, размещённая в сервисе Яндекс Бизнес, могла быть добавлена пользователями или найдена в общедоступных источниках. Она носит исключительно справочный характер.
Файл moscow_places.csv:
name — название заведения;address — адрес заведения;category — категория заведения, например «кафе», «пиццерия» или «кофейня»;hours — информация о днях и часах работы;lat — широта географической точки, в которой находится заведение;lng — долгота географической точки, в которой находится заведение;rating — рейтинг заведения по оценкам пользователей в Яндекс Картах (высшая оценка — 5.0);price — категория цен в заведении, например «средние», «ниже среднего», «выше среднего» и так далее;avg_bill — строка, которая хранит среднюю стоимость заказа в виде диапазона, например:middle_avg_bill — число с оценкой среднего чека, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Средний счёт»:middle_coffee_cup — число с оценкой одной чашки капучино, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Цена одной чашки капучино»:chain — число, выраженное 0 или 1, которое показывает, является ли заведение сетевым (для маленьких сетей могут встречаться ошибки):district — административный район, в котором находится заведение, например Центральный административный округ;seats — количество посадочных мест.# импортируем библиотеки
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import re
import plotly.express as px
from plotly import graph_objects as go
import json
from folium import Map, Choropleth
from folium import GeoJsonTooltip
import os
from folium import Map, Marker
from folium.plugins import MarkerCluster
# убираем предупреждения
pd.options.mode.chained_assignment = None # default='warn'
from matplotlib.axes._axes import _log as matplotlib_axes_logger
matplotlib_axes_logger.setLevel('ERROR')
# установим максимальное количество отображающихся столбцов
pd.set_option('display.max_columns', None)
# установим максимальную ширину отображающихся столбцов
pd.set_option('max_colwidth', 120)
# установим максимальное количество отображающихся элементов серии
np.set_printoptions(threshold=np.inf)
data¶# считаем данные
try:
data = pd.read_csv('/datasets/moscow_places.csv')
except:
data = pd.read_csv('moscow_places.csv')
data.head(5)
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | WoWфли | кафе | Москва, улица Дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | NaN | NaN | NaN | NaN | 0 | NaN |
| 1 | Четыре комнаты | ресторан | Москва, улица Дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | NaN | 0 | 4.0 |
| 2 | Хазри | кафе | Москва, Клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00 | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | NaN | 0 | 45.0 |
| 3 | Dormouse Coffee Shop | кофейня | Москва, улица Маршала Федоренко, 12 | Северный административный округ | ежедневно, 09:00–22:00 | 55.881608 | 37.488860 | 5.0 | NaN | Цена чашки капучино:155–185 ₽ | NaN | 170.0 | 0 | NaN |
| 4 | Иль Марко | пиццерия | Москва, Правобережная улица, 1Б | Северный административный округ | ежедневно, 10:00–22:00 | 55.881166 | 37.449357 | 5.0 | средние | Средний счёт:400–600 ₽ | 500.0 | NaN | 1 | 148.0 |
data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 8406 entries, 0 to 8405 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 8406 non-null object 1 category 8406 non-null object 2 address 8406 non-null object 3 district 8406 non-null object 4 hours 7870 non-null object 5 lat 8406 non-null float64 6 lng 8406 non-null float64 7 rating 8406 non-null float64 8 price 3315 non-null object 9 avg_bill 3816 non-null object 10 middle_avg_bill 3149 non-null float64 11 middle_coffee_cup 535 non-null float64 12 chain 8406 non-null int64 13 seats 4795 non-null float64 dtypes: float64(6), int64(1), object(7) memory usage: 919.5+ KB
Вывод
float64 и int, так и в текстовом формате objectprice и average_bill хранят категориальные данные, поэтому данные в этих столбцах представлены в текстовом формате object.Проверим наличие дублирующихся строк.
data[data.duplicated()]
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats |
|---|
Строки-дубликаты (явные/полные дубликаты) отсутствуют.
Также проверим наличие неявных дубликатов в столбце name для сетевых заведений (для несетевых заведений сложно угадать, это неявный дубликат или совершенно другое заведение).
data.query('chain==1')['name'].sort_values().unique()
array(['1-я Креветочная', '10 Идеальных Пицц', '18 Грамм', '4 Сезона',
'7 Сэндвичей', '8 Вафель', '9 Bar Coffee', 'ABC Coffee Roasters',
'Air Coffee', 'Americano Black Coffee & Food', 'Arabix',
'Arcus Bar And Food', 'Asia Gourmet', 'BB&Burgers', 'BFL’S',
'Bakery', 'Bfl’s', 'BigСуши', 'Black Star Burger', 'Bodrero',
'Bon Lavash', 'Boston seafood & bar', 'Bowl Family',
'Brasserie Lambic', 'Bro&n', 'Brooms', 'Burger Club',
'Burger Heroes', 'Bổ', 'CHICKO', 'Cafe', 'Cafe inn',
'Café de Paris', 'Camera Obscura', 'Camorra Pizza e Birra',
'Campus', 'Cassette Cafe', 'Chicha San Chen', 'Cinnabon',
'City life', 'CofeFest', 'Coffee Bean', 'Coffee Break',
'Coffee Guru', 'Coffee Like', 'Coffee Moose', 'Coffee Music',
'Coffee Party', 'Coffee Point', 'Coffee Way',
'Coffee and the City', 'Coffee in', "CoffeeBar'17", 'CoffeeShots',
'Coffeebrain', "Coffeekaldi's", 'Coffeeshop Company',
'Coffeesphere', 'Coffprice', 'Cofix', 'Conversation',
'Cosmic latte', 'Crop. coffee & smoothie bar',
'Cvc Китайская кухня', 'Deli2Go', 'Deli2go', 'Delimarche',
'Demi Coffee Shop', 'DimSum & Co', 'Dizengof99', 'Drive',
'Drive Café', "Dunkin' Donuts", 'Dон Хулио', 'Easy Pizza',
'Eat&Play', 'Eco шаурма', 'Eshak', 'Farш', 'Fibo Pasta & Ravioli',
'Finch', 'Fit Appėtit', 'Flip', 'Florentini',
'Food Low Cost Sushi', 'FoodBand.ru', 'Frank by Баста',
'Frankie Pizza', 'Free&co', 'French Bakery',
'French Bakery SeDelice', 'Fresh', 'Fry’d', 'Gagawa',
'Gentleman Coffee', 'Georgian Garden', 'GlowSubs', 'Grao De Cafe',
'Green V. A. I.', "Gump's", 'GøG', 'Halal food', 'Hatimaki',
'Hite', 'Ho Chu Pho', 'Home', 'Hot Dog Bulldog', 'Hq! Coffee',
'I Need Doner', 'I-cup', 'IL Патио', 'Ikigai', 'Il Letterato',
'Il Pittore', 'Ipho Cafe', 'Istanbul', "J'pan", "Jeffrey's Coffee",
"Jeffrey's Coffeeshop", 'Kafin', 'Karavan', 'Kaya Coffee Shop',
'Kimpab', 'Kitchen', 'Korean Chick', 'Krispy Kreme', 'Kulinari',
'Kuzina', 'Le Круассан', 'Leon', 'Les', 'Levin Bakehouse',
'Life Food', 'MamaMai', "Manny's Burger", 'Marketplace',
'Max Bakery', 'Menza', 'Milk&Beans', 'More Poke', 'Mátes',
'Naturality', 'Navat', 'Ngon', 'Nicepricecafe', 'Noba coffee',
'Nova bubble tea', 'Omg Coffee', 'One Price Coffee', 'One Special',
'One&Double', 'Osteria Mario', 'Paul', 'Pho', 'Pho Bo', 'Pho City',
'Pho Hanoi', 'Pho Ngon', 'Pho Oanh', 'Pho Street', 'Pho U',
'Pho Viet', 'PhoBo', 'Pims', 'Pizza Express 24', 'Pizza Hut',
'Plov.com', 'Ploveberry', 'Point 242', 'Poke house', 'Prime',
'Prolunch', 'Raw to Go', 'Remy Kitchen Bakery', 'Ricers',
'Right Habits', 'SUШi', 'Saperavi Cafe', 'Sattva',
'Scrocchiarella', 'SeDelice', 'Shawarma VIP House', 'Shwarm',
'Skuratov, coffee roasters', 'Soul in the Bowl', 'Sova Coffee',
'Spoon&Dagger', 'Star Hit Cafe', 'Steak it easy', 'Sub Cafe',
'Take and Wake', 'Tasty Thai', 'Temple Bar', 'The Best Burgers',
'The Hummus', 'The Wild Bean Cafe', 'The wild bean cafe',
'The Вареники', 'Tokpokki', 'Torro Grill', 'Tsomi', 'Tu Ton',
'Udcкафе', 'VASILCHUKÍ Chaihona №1', 'Viet Ngon', 'Viet Quan',
'Vintage', 'Vse-em', 'Vua Pho', 'Wave California Poke',
'We Cidreria', "Well's Home Cafe", 'White Fox', 'White Fox Cafe',
'Wild Bean', 'Wild Bean Cafe', 'Wild bean cafe', 'Winners',
'Wok Pho Mi', 'Wаурма', 'You&coffee', 'ZAcoffee', 'Zafferano',
'Zames', 'Zotman Pizza', 'Zамания', 'Zефир', 'Ё-ланч', 'Ёрш',
'Авокадо', 'АвтоСпаСтудия', 'Азбука daily', 'Азербайджан', 'Айва',
'Академия', 'Алло! Пицца', 'Алёнка', 'АндерСон', 'Андиамо',
'Антонио', 'Апельсин', 'Арамье', 'Арарат', 'Ача-Чача',
'БИБЛИОТЕКА Shisha Lounge', 'Базилик', 'Бакинский дворик', 'Баку',
'Бамбл Кофе', 'Бар-ресторан Территория', 'БарБариста', 'Баракат',
'Барбарис', 'Батони', 'Беседка', 'Бизнес-кафе', 'Бистро 24',
'Бишкек', 'БлинБери', 'Блинная', 'Бобры и утки', 'Бота', 'Бранч',
'Брусника', 'Булка', 'Булкер', 'Булочная № 5', 'Булошная',
'Булошная № 1', 'Бульбяная', 'Бульвар', 'Бургер Кинг',
'Буркина фасоль', 'Буханка', 'Буше', 'Быстрое питание',
'Бюро Пиццы', 'Бёргер Стейк', 'В парке вкуснее', 'В своей тарелке',
'Вай Мэ!', 'Ванильное небо', 'Вареничная № 1', 'Везувио', 'Венахи',
'Веранда', 'Верона', 'Весна', 'Виват Пицца', 'Виктория', 'Виолино',
'Вкус Востока', 'Вкус Дня', 'Вкус Индии', 'Вкус востока',
'Вкус дня', 'Воккер', 'Волконский', 'Восток', 'Восточная кухня',
'Вояж', 'Встреча', 'Выпечка', 'ВьетКафе', 'Вьетнамская кухня',
'Вьетнамское кафе', 'Галерея вкуса', 'Гамбринус',
'Гастро Бистро Шаверма-Братуха', 'Гастробар', 'Гедонист',
'Генацвале', 'Гладиатор', 'Голубка', 'Гораздо', 'Городок',
'Городское', 'Горячая выпечка', 'Грабли', 'Гранат', 'Гриль Хаус',
'Гриль парк', 'Грузинская кухня', 'Груша', 'Гудман', 'Гурман',
'Гурманика', 'Гурмэ Ланч', 'Да, еда', 'ДаблБи', 'Даблби',
'Дагестанская лавка', 'Дайнинг Холл', 'Дастархан', 'Дворик',
'Дежене', 'Джаганнат', 'Джонджоли', 'Диана', 'Диван', 'Додо Пицца',
'Домашние обеды', 'Домашний вкус', 'Домашняя еда',
"Домино'с Пицца", 'Донер', 'Донер Кебаб', 'Донер Хаус',
'Донер в пите', 'Донер кебаб', 'Донер хаус', 'Дружба', 'Дымок',
'Дядюшка ХО', 'Евразия', 'Еврокафе', 'Еда', 'Еда Greek',
'Ели Сацебели', 'Есть Хинкали&Пить Вино', 'Жан-Жак', 'Жига Дрыга',
'Жираф', 'За обе щёки', 'Зандукели', 'Зарафшон', 'Здрасте',
'Зефир', 'Иди обниму', 'Изи Паб', 'Изюм', 'Иль Марко',
'Илья Муромец', 'Империя', 'Империя Пиццы', 'Индийская точка',
'Искра', 'Итальянский ресторан DaPino', 'КОФЕПОРТ', 'Кабуки',
'Кабул', 'Как Дома', 'Как дома', 'Калина', 'Калитки', 'Кампус',
'Капучино Кидс', 'Караван', 'Кариночка', 'Карло', 'Катюша',
'Кафе бар', 'Кафе-Столовая', 'Кафе-бар', 'Кафе-пекарня',
'Кафетерий', 'Кафетериус', 'Кафетеррия', 'Кафешка', 'Кахури',
'Квартира 44', 'Кинг Авто', 'Кинобар', 'Кинто', 'Китайская кухня',
'Китайский ресторан', 'Китчен', 'Клёво', 'Колбасофф', 'Ком 1989',
'Кондитерская Олега Ильина', 'Кондитерская-кулинария Брусника',
'Корчма Тарас Бульба', 'Кофе', 'Кофе & Moloko', 'Кофе Хауз',
'Кофе пью', 'Кофе с собой', 'Кофе твой друг', 'КофеТИ',
'КофеТун-СушиТун', 'Кофедей', 'Кофейник', 'Кофемания', 'Крепери',
'Крошка Картошка', 'Крошка картошка', 'Кружка Паб', 'Кулинариум',
'Кулинариум - кулинария, пироги, салаты', 'Кулинария',
'Кулинарная лавка братьев Караваевых', 'Кухня Полли', 'Кушавель',
'Кушай Город', 'Ла Гатта', 'ЛавашОК', 'Лагман', 'Лагман Хаус',
'Лаззат', 'Лаки Сувлаки', 'Ламаджо', 'Ланч поинт', 'Лао Ли',
'Легенда', 'Лента Онлайн', 'Лепим и Варим', 'Лепим и варим', 'Лес',
'Летняя веранда', 'Лечо', 'Ли', 'Ливан Хаус', 'Лимонадница',
'Линдфорс', 'Литературное кафе', 'Лоза', 'Луна', 'Луч',
'Любовь и Сладости', 'Ля Фантази', 'М2 Органик', 'МСК Lounge',
'Магбургер', 'Магнолия', 'Мадина', 'Маковка',
'Маленькая пекарня Журавлевых', 'Малетон', 'Манас', 'Мангал',
'Маргарита', 'Марков двор', 'Мархал', 'Масала Хаус', 'Матрёшка',
'Маяк', 'Мельница', 'Меркато', 'Мимино', 'Миндаль', 'Мир шашлыков',
'Мисада', 'Мишель', 'Мое кафе', 'Может, кофе?',
'Монастырская трапеза', 'МореМоре', 'Моремания', 'МосПлов',
'Моё кафе', 'Му-Му', 'Музейное кафе', 'Мюнгер', 'Мясо на углях',
'Мясо&Паста', 'Мясо&Рыба', 'Мята Lounge', 'Нам', 'Натахтари',
'Находка', 'Нейборс', 'Неслучайно 08 08', 'Нияма',
'Нью-Йорк пицца и гриль', 'ОШ', 'Оазис', 'Обедов', 'Огонёк',
'Одесса-мама', 'Оливка', 'Оля', 'Омореморе', 'Ором', 'Орхан',
'Остров', 'Островок', 'Островок Суши', 'Очаг', 'Пан Запекан',
'Папа Джонс', 'Парк', 'Парус', 'Пекарня', 'Пекарня Буханка',
'Пекарня № 1', 'Пельменная', 'Перекрёсток', 'Печорин', 'Пивбар',
'Пивной ресторан Пив&Ко', 'Пикник', 'Пион', 'Пипони', 'Пирог Хауз',
'Питербургер', 'Пицца Паоло', 'Пицца и Канноли', 'Пицца на районе',
'Пицца экспресс', 'Пиццаменто', 'Плов', 'Плов центр', 'Подсолнух',
'Полянка', 'Поминальная трапеза', 'Пончики',
'Пончики! Выпекаем на месте', 'Пороселло', 'Правда Кофе',
'Правда кофе', 'Праймбиф бар', 'Прованс', 'Прогресс', 'Пронто',
'Профессор Пуф', 'ПтиШу', 'Публика', 'Пшеница', 'Пян-се',
'Пять звёзд', 'Работа', 'Радуга', 'Ракета',
'Раковарня Иван Раковар', 'Раковарня Клешни и хвосты', 'Раковая',
'Рамен Тен', 'Рамен-Клаб', 'Рандеву', 'Реберная № 1', 'Регистан',
'Ренессанс', 'Ресторан Мацони', 'Роко Бэй — Мох и кофе', 'Роллофф',
'Рома', 'Роса', 'Рубим Бургер', 'Руккола',
'Рыбная мануфактура № 1', 'Рэдимэйд', 'Рябина', 'СССР', 'Садовод',
'Сакура', 'Салют', 'Сам пришёл', 'Самарканд', 'Самарканд Сити',
'Самса № 1', 'Сварня', 'Свежая выпечка', 'Свежъ',
'Свидание на крыше', 'Север-Метрополь', 'Сезоны', 'Семейное кафе',
'Семейный Очаг', 'Сеть поминальных залов', 'Сикварули', 'Синнабон',
'Сириус', 'Сити Лайф', 'Сити Пицца', 'Сицилия', 'Сказка', 'Скалка',
'Смак', 'Снеди Феди', 'Соль', 'Соседи', 'Стардогс', 'Старый Баку',
'Старый Сычуань', 'Старый город', 'Старый дворик',
'Стейк & Бургер', 'Столичный вкус', 'Столовая № 1',
'Столовая-кафе Росинка', 'Страдивари', 'Сундук', 'Сусеки',
'Суши Love', 'Суши Wok', 'Суши Сет', 'Суши Таун', 'Суши-Пицца 312',
'СушиСтор', 'Сыроварня', 'Сыто Пьяно', 'Сытый гусь', 'Тандыр',
'Тандыр № 1', 'Тануки', 'Тапчан', 'Тарелка', 'Татнефть кафе',
'Ташир пицца', 'Тбилисо', 'Тбилисоба', 'Твой кофе', 'Теремок',
'Тирольские пироги', 'Ткемали', 'Топ Chick', 'Точка', 'Трактир',
'ТрапеZа', 'Траттория Венеция', 'Трдельникъ', 'Турецкая лавка',
'Уголок', 'Удача', 'Узбечка', 'Уйгурский лагман', 'Урюк', 'Уют',
'Фергана', 'Филин', 'Фортуна', 'Франклинс Бургер',
'Французская пекарня', 'Фрателло', 'Фудмаркет', 'Халал', 'Халва',
'Халва, Сеть Почтоматов', 'Халяль', 'Хан Кебаб', 'Харчевниковъ',
'Хачапури', 'Хачапури и вино', 'Хаят', 'Хинкали',
'Хинкали - Gали!', 'Хинкали и Вино', 'Хинкали-Gали!', 'Хинкальная',
'Хинкальная Легенда', 'Хинкальная Экспресс', 'Хинкальная № 1',
'Хинкальный дом', 'Хлеб & Co', 'Хлеб Насущный', 'Хлеб да Выпечка',
'Хлеб и Вино', 'Хлеб насущный', 'Хлеб с маслом',
'Хлеба&Зрелищ пиццерия', 'Хлебница', 'Хорошее место',
'Хочу шашлык', 'Чабан Чуду', 'Чай', 'Чайка', 'Чайная',
'Чайная высота', 'Чайхана', 'Чайхана 24', 'Чайхана Sabr',
'Чайхана Азия', 'Чайхана Баракат', 'Чайхана Бишкек сити',
'Чайхана Манас', 'Чайхана ОШ', 'Чайхана Самарканд',
'Чайхана Ташкент', 'Чайхана Халаль', 'Чайхана Халва',
'Чайхана Халяль', 'Чайхана Хан', 'Чайхана Элина', 'Чайхана халяль',
'Чайхана-24', 'Чайхона', 'Чайхона Айва', 'Чайхона № 1',
'Чайхона №1', 'Чаме-Чаме', 'Чао-пицца', 'Чебуреки Манты',
'Чебуречная СССР', 'Чебуречная история', 'Черетто', 'Черетто Море',
'Чинар', 'Чито-Ра', 'Чихо', 'Шаверма', 'Шаурма 24',
'Шаурма в пите', 'Шаурма на углях', 'Шафран', 'Шашлык',
'Шашлык Хаус', 'Шашлыки', 'ШашлыкоFF', 'Шашлычная № 1',
'Шашлычный двор', 'Шашлычный дворик', 'Шашлычок', 'Швили',
'Шеф Бургер', 'Шик Шашлык', 'Шоколадница', 'Шоти', 'Шпинат',
'Штолле', 'Щепка', 'Эзо', 'Эль кафе', 'Юг', 'Южный дворик',
'Юность', 'Юрта', 'Я люблю суши', 'Ян Примус', 'Яндекс Лавка',
'Яндекс.Лавка', 'Японская кухня'], dtype=object)
В стобце name встречаются разные названия одной и той же сети:
Переименуем их на уникальное название.
data['name'] = data['name'].replace(['Яндекс Лавка', 'Яндекс.Лавка'], 'Яндекс.Лавка')
data['name'] = data['name'].replace(['Чайхона № 1', 'Чайхона №1'], 'Чайхона №1')
data['name'] = data['name'].replace(['Чайхана Халаль', 'Чайхана Халяль', 'Чайхана халяль'], 'Чайхана Халяль')
data['name'] = data['name'].replace(["Домино'с Пицца", "Домино'с пицца", "Доминос пицца"], "Домино'с Пицца")
for column in data.columns:
if data[column].isna().sum()>0:
print("Название столбца:", column)
print("Количество пропусков:", data[column].isna().sum())
print('----')
print("В остальных столбцах пропуски отсутствуют")
Название столбца: hours Количество пропусков: 536 ---- Название столбца: price Количество пропусков: 5091 ---- Название столбца: avg_bill Количество пропусков: 4590 ---- Название столбца: middle_avg_bill Количество пропусков: 5257 ---- Название столбца: middle_coffee_cup Количество пропусков: 7871 ---- Название столбца: seats Количество пропусков: 3611 ---- В остальных столбцах пропуски отсутствуют
Пропуски в столбцах hours, price, avg_bill и seats скорее всего обозначает отсутствие данных о времени работы заведения, поэтому заполним пропуски значением no_info.
# заполняем пропуски и nan в столбцах hours, price, avg_bill и seats значением 'no_info'
data[['hours', 'price', 'avg_bill', 'seats']] = data[['hours', 'price', 'avg_bill', 'seats']].fillna('no_info')
Пропуски в столбце middle_avg_bill могут означать, что столбец avg_bill не начинаетя с подстроки «Средний счёт», а пропуски в столбце middle_coffee_cup - что столбец avg_bill не начинаетя с подстроки «Цена одной чашки капучино». Если эта догадка верная, заменим пропуски значением N/A - not applicable.
# проверим условие для столбца middle_avg_bill
if data[~data['avg_bill'].str.startswith("Средний счёт")]['middle_avg_bill'].isna().sum() == data['middle_avg_bill'].isna().sum():
print('Пропуски в столбце middle_avg_bill встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются')
print('с подстроки «Средний счёт». Пропуски заменили значением N\A.')
# заменим пропуски на 'N\A'
data['middle_avg_bill'] = data['middle_avg_bill'].fillna('N/A')
else:
print('Догадка неверная.')
print('----')
# проверим условие для столбца middle_coffee_cup
if data[~data['avg_bill'].str.startswith("Цена одной чашки капучино")]['middle_coffee_cup'].isna().sum() == data['middle_coffee_cup'].isna().sum():
print('Пропуски в столбце middle_coffee_cup встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются')
print('с подстроки «Цена одной чашки капучино». Пропуски заменили значением N\A.')
# заменим пропуски на 'N\A'
data['middle_coffee_cup'] = data['middle_coffee_cup'].fillna('N/A')
else:
print('Догадка неверная.')
Пропуски в столбце middle_avg_bill встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются с подстроки «Средний счёт». Пропуски заменили значением N\A. ---- Пропуски в столбце middle_coffee_cup встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются с подстроки «Цена одной чашки капучино». Пропуски заменили значением N\A.
street с названиями улиц из столбца с адресом¶data['street'] = data['address'].apply(lambda x: x.split(',')[1].strip())
data[['street']]
| street | |
|---|---|
| 0 | улица Дыбенко |
| 1 | улица Дыбенко |
| 2 | Клязьминская улица |
| 3 | улица Маршала Федоренко |
| 4 | Правобережная улица |
| ... | ... |
| 8401 | Профсоюзная улица |
| 8402 | Пролетарский проспект |
| 8403 | Люблинская улица |
| 8404 | Люблинская улица |
| 8405 | Россошанский проезд |
8406 rows × 1 columns
is_24_7 с обозначением, что заведение работает ежедневно и круглосуточно¶data['is_24_7'] = data['hours'] == 'ежедневно, круглосуточно'
data[['is_24_7']]
| is_24_7 | |
|---|---|
| 0 | False |
| 1 | False |
| 2 | False |
| 3 | False |
| 4 | False |
| ... | ... |
| 8401 | False |
| 8402 | False |
| 8403 | True |
| 8404 | True |
| 8405 | True |
8406 rows × 1 columns
distr_short с аббревиатурой для каждого района¶# Функция для преобразования значения в аббревиатуру
def get_initials(text):
words = text.replace('-', ' ').split()
initials = ""
for word in words:
initials += word[0].upper()
return initials
# Создание столбца с аббревиатурами
data['distr_short'] = data['district'].apply(get_initials)
data['distr_short'].unique()
array(['САО', 'СВАО', 'СЗАО', 'ЗАО', 'ЦАО', 'ВАО', 'ЮВАО', 'ЮАО', 'ЮЗАО'],
dtype=object)
Вывод
Итоговый датасет после предобработки данных и с новыми столбцами выглядит следующим образом.
data
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | is_24_7 | distr_short | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | WoWфли | кафе | Москва, улица Дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | no_info | no_info | N/A | N/A | 0 | no_info | улица Дыбенко | False | САО |
| 1 | Четыре комнаты | ресторан | Москва, улица Дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | N/A | 0 | 4.0 | улица Дыбенко | False | САО |
| 2 | Хазри | кафе | Москва, Клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00 | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | N/A | 0 | 45.0 | Клязьминская улица | False | САО |
| 3 | Dormouse Coffee Shop | кофейня | Москва, улица Маршала Федоренко, 12 | Северный административный округ | ежедневно, 09:00–22:00 | 55.881608 | 37.488860 | 5.0 | no_info | Цена чашки капучино:155–185 ₽ | N/A | 170.0 | 0 | no_info | улица Маршала Федоренко | False | САО |
| 4 | Иль Марко | пиццерия | Москва, Правобережная улица, 1Б | Северный административный округ | ежедневно, 10:00–22:00 | 55.881166 | 37.449357 | 5.0 | средние | Средний счёт:400–600 ₽ | 500.0 | N/A | 1 | 148.0 | Правобережная улица | False | САО |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 8401 | Суши Мания | кафе | Москва, Профсоюзная улица, 56 | Юго-Западный административный округ | ежедневно, 09:00–02:00 | 55.670021 | 37.552480 | 4.4 | no_info | no_info | N/A | N/A | 0 | 86.0 | Профсоюзная улица | False | ЮЗАО |
| 8402 | Миславнес | кафе | Москва, Пролетарский проспект, 19, корп. 1 | Южный административный округ | ежедневно, 08:00–22:00 | 55.640875 | 37.656553 | 4.8 | no_info | no_info | N/A | N/A | 0 | 150.0 | Пролетарский проспект | False | ЮАО |
| 8403 | Самовар | кафе | Москва, Люблинская улица, 112А, стр. 1 | Юго-Восточный административный округ | ежедневно, круглосуточно | 55.648859 | 37.743219 | 3.9 | no_info | Средний счёт:от 150 ₽ | 150.0 | N/A | 0 | 150.0 | Люблинская улица | True | ЮВАО |
| 8404 | Чайхана Sabr | кафе | Москва, Люблинская улица, 112А, стр. 1 | Юго-Восточный административный округ | ежедневно, круглосуточно | 55.648849 | 37.743222 | 4.2 | no_info | no_info | N/A | N/A | 1 | 150.0 | Люблинская улица | True | ЮВАО |
| 8405 | Kebab Time | кафе | Москва, Россошанский проезд, 6 | Южный административный округ | ежедневно, круглосуточно | 55.598229 | 37.604702 | 3.9 | no_info | no_info | N/A | N/A | 0 | 12.0 | Россошанский проезд | True | ЮАО |
8406 rows × 17 columns
# Определяем порядок сортировки
sorted_data1 = data['category'].value_counts().sort_values(ascending=False).reset_index()
sorted_data1.columns = ['category', 'count']
# Задаем размер графика
plt.figure(figsize=(8, 6))
# Создаем график
graph1 = sns.barplot(x='category', y='count', data=sorted_data1)
# Поворачиваем подписи на 45 градусов
labels1 = graph1.get_xticklabels()
graph1.set_xticklabels(labels1, rotation=45)
# Добавляем заголовок и подписи осей
plt.title('Распределение заведений по категориям')
plt.xlabel('Категории заведений')
plt.ylabel('Количество')
# Отображаем график
plt.show()
Выводы
# Фильтруем только те заведения, где имеется информация о количестве стульев
sorted_data2 = data.query('seats != "no_info"')
# Сохранияем информацию о стульях в формате int
sorted_data2['seats'] = sorted_data2['seats'].astype(int)
# Определяем порядок сортировки
sorted_data2 = sorted_data2.groupby('category').agg({'seats': 'median'}).sort_values('seats', ascending=False).reset_index()
# Задаем размер графика
plt.figure(figsize=(8, 6))
# Создаем график
graph2 = sns.barplot(x='category', y='seats', data=sorted_data2)
# Поворачиваем подписи на 45 градусов
labels2 = graph2.get_xticklabels()
graph2.set_xticklabels(labels2, rotation=45)
# Добавляем значения над каждым столбцом
for index, row in sorted_data2.iterrows():
graph2.annotate('{:.0f}'.format(row['seats']), xy=(index, row['seats']), ha='center', va='bottom')
# Добавляем заголовок и подписи осей
plt.title('Медианное количество посадочных мест в заведениях')
plt.xlabel('Категории заведений')
plt.ylabel('Медианное количество мест')
# Отображаем график
plt.show()
Выводы
# готовим данные для графика
sorted_data3 = data['chain'].value_counts().sort_values(ascending=False).reset_index()
sorted_data3.columns = ['type', 'count']
sorted_data3
| type | count | |
|---|---|---|
| 0 | 0 | 5201 |
| 1 | 1 | 3205 |
# готовим данные для графика
sorted_data3['type2'] = sorted_data3['type']
sorted_data3['type2'] = sorted_data3['type2'].replace([0, 1], ['несетевое', 'сетевое'])
# строим круговую диаграмму
fig3 = go.Figure(data=[go.Pie(labels=sorted_data3['type2'], # указываем значения, которые появятся на метках сегментов
values=sorted_data3['count'], # указываем данные, которые отобразятся на графике
pull = [0.1, 0])]) # добавляем аргумент, который выделит тип-лидер на графике
fig3.update_layout(title='Соотношение сетевых и несетевых заведений', # указываем заголовок графика
width=700, # указываем размеры графика
height=500,
annotations=[dict(x=1.12, # вручную настраиваем аннотацию легенды
y=1.05,
text='Количество заведений',
showarrow=False)])
fig3.show() # выводим график
Вывод
# Подготовим данные для графика
sorted_data4 = data.groupby(['category', 'chain']).agg({'name': 'count'}).reset_index()
sorted_data4['chain'] = sorted_data4['chain'].replace([0, 1], ['несетевое', 'сетевое'])
sorted_data4.rename(columns={'name': 'count'}, inplace=True)
sorted_data4 = sorted_data4.sort_values('count', ascending=False).reset_index()
# Задаем размер графика
plt.figure(figsize=(10, 7))
# Создаем график
graph4 = sns.barplot(x='category', y='count', hue='chain', data=sorted_data4)
# Поворачиваем подписи на 45 градусов
labels4 = graph4.get_xticklabels()
graph4.set_xticklabels(labels4, rotation=45)
# Убираем надпись 'chain' из легенды
graph4.legend(title=None)
# Добавляем заголовок и подписи осей
plt.title('Категории сетевых и несетевых заведений')
plt.xlabel('Категории')
plt.ylabel('Количество заведений')
# Отображаем график
plt.show()
Вывод
Сетевыми заведениями чаще являются кофейни, пицерии и булочные. У остальных категорий заведений чаще встречаются несетевые типы.
# Подготовим данные для графика
sorted_data5 = data.query('chain==1')
sorted_data5 = sorted_data5.groupby(['name', 'category']).agg({'address': 'count'}).reset_index()
sorted_data5.rename(columns={'address': 'count'}, inplace=True)
sorted_data5 = sorted_data5.sort_values('count', ascending=False).reset_index().drop('index', axis=1)
sorted_data5 = sorted_data5.head(15)
sorted_data5
| name | category | count | |
|---|---|---|---|
| 0 | Шоколадница | кофейня | 119 |
| 1 | Домино'с Пицца | пиццерия | 76 |
| 2 | Додо Пицца | пиццерия | 74 |
| 3 | Яндекс.Лавка | ресторан | 72 |
| 4 | One Price Coffee | кофейня | 71 |
| 5 | Cofix | кофейня | 65 |
| 6 | Prime | ресторан | 49 |
| 7 | КОФЕПОРТ | кофейня | 42 |
| 8 | Кулинарная лавка братьев Караваевых | кафе | 39 |
| 9 | Теремок | ресторан | 36 |
| 10 | CofeFest | кофейня | 31 |
| 11 | Чайхана | кафе | 26 |
| 12 | Буханка | булочная | 25 |
| 13 | Drive Café | кафе | 24 |
| 14 | Кофемания | кофейня | 22 |
# строим столбчатую диаграмму
fig5 = px.bar(sorted_data5.sort_values(by='count', ascending=True), # загружаем данные и заново их сортируем
x='count', # указываем столбец с данными для оси X
y='name', # указываем столбец с данными для оси Y
text='count', # добавляем аргумент, который отобразит текст с информацией о количестве
hover_data=['category'] # добавляем аргумент, который отобразит категорию при наведении курсора
)
# оформляем график
fig5.update_layout(title='ТОП-15 популярных сетей по количеству заведений',
xaxis_title='Количество заведений',
yaxis_title='Название сети')
fig5.show() # выводим график
print('ТОП-15 популярных сетей по количеству заведений относятся к следующим категориям:', sorted_data5['category'].unique())
ТОП-15 популярных сетей по количеству заведений относятся к следующим категориям: ['кофейня' 'пиццерия' 'ресторан' 'кафе' 'булочная']
Выводы
# Подготовим данные для графика
sorted_data6 = data.groupby(['distr_short', 'category']).agg({'address': 'count'}).reset_index()
sorted_data6.rename(columns={'address': 'count'}, inplace=True)
sorted_data6 = sorted_data6.sort_values('count', ascending=False).reset_index().drop('index', axis=1)
sorted_data6_distr = sorted_data6.groupby(['distr_short']).agg({'count': 'sum'}).reset_index()
sorted_data6 = pd.merge(sorted_data6, sorted_data6_distr, on='distr_short')
sorted_data6.rename(columns={'count_x': 'count_cat', 'count_y': 'count_distr'}, inplace=True)
# Отсортируем данные
sorted_data6 = sorted_data6.sort_values(by=['count_distr', 'count_cat'], ascending=False)
# Создание столбчатой диаграммы
fig6 = px.bar(sorted_data6, x='distr_short', y='count_cat', color='category', labels={
'category': 'Категория заведения',
'distr_short': 'Административный район',
'count_cat': 'Количество заведений'
})
# Настройка внешнего вида диаграммы
fig6.update_layout(title='Количество заведений по районам и категориям',
xaxis_title='Административный район',
yaxis_title='Количество заведений')
# Установка размера графика
fig6.update_layout(width=950, height=600)
fig6.show()
print('В датасете присутствуют', data['district'].nunique(), 'административных районов:', data['district'].unique())
В датасете присутствуют 9 административных районов: ['Северный административный округ' 'Северо-Восточный административный округ' 'Северо-Западный административный округ' 'Западный административный округ' 'Центральный административный округ' 'Восточный административный округ' 'Юго-Восточный административный округ' 'Южный административный округ' 'Юго-Западный административный округ']
Выводы
# Определяем порядок сортировки
sorted_data7 = data.groupby('category').agg({'rating': 'median'}).sort_values('rating', ascending=False).reset_index()
# Задаем размер графика
plt.figure(figsize=(8, 6))
# Создаем график
graph7 = sns.barplot(x='category', y='rating', data=sorted_data7)
# Поворачиваем подписи на 45 градусов
labels7 = graph7.get_xticklabels()
graph7.set_xticklabels(labels7, rotation=45)
# Добавляем значения над каждым столбцом
for index, row in sorted_data7.iterrows():
graph7.annotate('{:.1f}'.format(row['rating']), xy=(index, row['rating']), ha='center', va='bottom')
# ограничиваем ось Y для наглядности
plt.ylim(3.8, 4.5)
# Добавляем заголовок и подписи осей
plt.title('Медианные рейтинги заведений')
plt.xlabel('Категории заведений')
plt.ylabel('Медианный рейтинг')
# Отображаем график
plt.show()
# Задаем размер графика
plt.figure(figsize=(9, 8))
# применяем стиль darkgrid из библиотеки seaborn
sns.set_style('darkgrid')
# строим график boxplot средствами seaborn
sns.boxplot(x='rating', y='category', data=data)
# указываем заголовок графика и подписи осей средствами matplotlib
plt.title('Распределение средней оценки в зависимости категории заведений')
plt.xlabel('Средняя оценка')
plt.ylabel('Категории заведений')
# отображаем график на экране
plt.show()
Выводы
В датасете представлено 9 округов.
Для каждого округа посчитаем медианный рейтинг торговых центров, которые находятся на его территории.
rating_data = data.groupby('district', as_index=False)['rating'].agg('median').sort_values(by='rating', ascending=False)
rating_data
| district | rating | |
|---|---|---|
| 5 | Центральный административный округ | 4.4 |
| 0 | Восточный административный округ | 4.3 |
| 1 | Западный административный округ | 4.3 |
| 2 | Северный административный округ | 4.3 |
| 4 | Северо-Западный административный округ | 4.3 |
| 7 | Юго-Западный административный округ | 4.3 |
| 8 | Южный административный округ | 4.3 |
| 3 | Северо-Восточный административный округ | 4.2 |
| 6 | Юго-Восточный административный округ | 4.2 |
# загружаем JSON-файл с границами округов Москвы
pth1 = '/datasets/admin_level_geomap.geojson'
pth2 = 'admin_level_geomap.geojson'
if os.path.exists(pth1):
state_geo = pth1
elif os.path.exists(pth2):
state_geo = pth2
else:
print('Something is wrong')
# moscow_lat - широта центра Москвы, moscow_lng - долгота центра Москвы
moscow_lat, moscow_lng = 55.751244, 37.618423
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=rating_data,
columns=['district', 'rating'],
key_on='feature.name',
fill_color='YlGn',
fill_opacity=0.8,
legend_name='Медианный рейтинг заведений по районам',
).add_to(m)
# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m)
# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
Marker(
[row['lat'], row['lng']],
popup=f"{row['name']} {row['rating']}",
).add_to(marker_cluster)
# применяем функцию create_clusters() к каждой строке датафрейма
data.apply(create_clusters, axis=1)
# выводим карту
m
sorted_data8 = data.groupby(['street', 'category']).agg({'address': 'count'}).reset_index()
sorted_data8.rename(columns={'address': 'count'}, inplace=True)
sorted_data8 = sorted_data8.sort_values('count', ascending=False).reset_index().drop('index', axis=1)
top_15_str = sorted_data8.groupby(['street']).agg({'count': 'sum'}).sort_values('count', ascending=False).reset_index().head(15)
top_15_str
| street | count | |
|---|---|---|
| 0 | проспект Мира | 184 |
| 1 | Профсоюзная улица | 122 |
| 2 | проспект Вернадского | 108 |
| 3 | Ленинский проспект | 107 |
| 4 | Ленинградский проспект | 95 |
| 5 | Дмитровское шоссе | 88 |
| 6 | Каширское шоссе | 77 |
| 7 | Варшавское шоссе | 76 |
| 8 | Ленинградское шоссе | 70 |
| 9 | МКАД | 65 |
| 10 | Люблинская улица | 60 |
| 11 | улица Вавилова | 55 |
| 12 | Кутузовский проспект | 54 |
| 13 | улица Миклухо-Маклая | 49 |
| 14 | Пятницкая улица | 48 |
# Подготовим данные для графика
sorted_data8 = sorted_data8[sorted_data8['street'].isin(top_15_str['street'])]
sorted_data8 = pd.merge(sorted_data8, top_15_str, on='street')
sorted_data8.rename(columns={'count_x': 'count_cat', 'count_y': 'count_str'}, inplace=True)
# Отсортируем данные
sorted_data8 = sorted_data8.sort_values(by=['count_str', 'count_cat'], ascending=False)
# Создание столбчатой диаграммы
fig8 = px.bar(sorted_data8, x='street', y='count_cat', color='category', labels={
'category': 'Категория заведения',
'street': 'Улица',
'count_cat': 'Количество заведений'
})
# Настройка внешнего вида диаграммы
fig8.update_layout(title='ТОП-15 улиц по количеству заведений',
xaxis_title='Улица',
yaxis_title='Количество заведений')
fig8.show()
Выводы
# Выявляем улицы, где есть только 1 заведение
sorted_data9 = data.groupby(['street']).agg({'address': 'count'}).query('address==1').reset_index()
print('Всего улиц, на которых находится только один объект общепита:', sorted_data9['street'].nunique())
print()
print(sorted_data9['street'].unique())
Всего улиц, на которых находится только один объект общепита: 458 ['1-й Автозаводский проезд' '1-й Балтийский переулок' '1-й Варшавский проезд' '1-й Вешняковский проезд' '1-й Голутвинский переулок' '1-й Грайвороновский проезд' '1-й Дербеневский переулок' '1-й Земельный переулок' '1-й Капотнинский проезд' '1-й Кирпичный переулок' '1-й Колобовский переулок' '1-й Котляковский переулок' '1-й Курьяновский проезд' '1-й Николощеповский переулок' '1-й Новокузнецкий переулок' '1-й Рижский переулок' '1-й Самотёчный переулок' '1-й Сетуньский проезд' '1-й Спасоналивковский переулок' '1-й Щипковский переулок' '1-я Боевская улица' '1-я Курьяновская улица' '1-я Парковая улица' '1-я Стекольная улица' '1-я Фрунзенская улица' '1-я Ямская улица' '1-я линия Хорошёвского Серебряного Бора' '11-я Парковая улица' '12-я Парковая улица' '14-я Парковая улица' '16-я Парковая улица' '17-й проезд Марьиной Рощи' '2-й Балтийский переулок' '2-й Боткинский проезд' '2-й Верхний Михайловский проезд' '2-й Вышеславцев переулок' '2-й Зачатьевский переулок' '2-й Кожуховский проезд' '2-й Красносельский переулок' '2-й Крестовский переулок' '2-й Лучевой просек' '2-й Новоподмосковный переулок' '2-й Обыденский переулок' '2-й Полевой переулок' '2-й Рощинский проезд' '2-й Силикатный проезд' '2-й Тверской-Ямской переулок' '2-й Хорошёвский проезд' '2-й Щемиловский переулок' '2-й квартал Капотни' '2-й квартал Капотня' '2-й переулок Петра Алексеева' '2-я Владимирская улица' '2-я Звенигородская улица' '2-я Карачаровская улица' '2-я Магистральная улица' '2-я Пугачёвская улица' '2-я Рощинская улица' '2-я Рыбинская улица' '2-я Фрезерная улица' '2-я улица Марьиной Рощи' '3-й Кадашёвский переулок' '3-й Лихачёвский переулок' '3-й Михалковский переулок' '3-й Новомихалковский проезд' '3-й Хорошёвский проезд' '3-й проезд Перова Поля' '3-й проезд Подбельского' '3-я Радиаторская улица' '3-я Тверская-Ямская улица' '3-я Фрунзенская улица' '4-й Воробьёвский проезд' '4-й Лесной переулок' '4-й Рощинский проезд' '4-я Кабельная улица' '4-я улица 8 Марта' '5-й проезд Подбельского' '5-я Магистральная улица' '5-я улица Ямского Поля' '6-й Лучевой просек' '6-я Парковая улица' '9-я улица Соколиной Горы' 'Алтайская улица' 'Андреевский пешеходный мост' 'Андроньевская площадь' 'Анненский проезд' 'Арбатский переулок' 'Архангельский переулок' 'Балакиревский переулок' 'Барвихинская улица' 'Басманный тупик' 'Беговая аллея' 'Бесединское шоссе' 'Бибиревская улица' 'Бобров переулок' 'Богословский переулок' 'Большая Калитниковская улица' 'Большая Пионерская улица' 'Большая Тихоновская улица' 'Большой Волоколамский проезд' 'Большой Кисловский переулок' 'Большой Козловский переулок' 'Большой Николопесковский переулок' 'Большой Строченовский переулок' 'Большой Сухаревский переулок' 'Большой Трёхгорный переулок' 'Борисовская улица' 'Боровское шоссе' 'Ботанический переулок' 'Ботанический сад Московского государственного университета' 'Боярский переулок' 'Братеевский парк' 'Братиславский парк' 'Брянская улица' 'Будайский проезд' 'Булатниковский проезд' 'Бутиковский переулок' 'Вагоноремонтная улица' 'Валдайский проезд' 'Васильевская улица' 'Верхнелихоборская улица' 'Верхний Сусальный переулок' 'Верхняя улица' 'Верхоянская улица' 'Весёлая улица' 'Воробьёвская набережная' 'Ворошиловский парк' 'Выползов переулок' 'Гаврикова улица' 'Гагаринский переулок' 'Гагаринский тоннель' 'Газгольдерная улица' 'Гжельский переулок' 'Главный ботанический сад имени Н.В. Цицина Российской академии наук' 'Глубокий переулок' 'Городецкая улица' 'Городская улица' 'Гороховский переулок' 'Графский переулок' 'Грузинский сквер' 'Гурьевский проезд' 'Даев переулок' 'Девяткин переулок' 'Делегатская улица' 'Дендропарковая улица' 'Денежный переулок' 'Дербеневская улица' 'Дмитровский переулок' 'Дорогобужская улица' 'Дурасовский переулок' 'Ереванская улица' 'Живарёв переулок' 'Жуков проезд' 'Загорьевский проезд' 'Задонский проезд' 'Замоскворецкая линия' 'Заозёрная улица' 'Зарайская улица' 'Звенигородская улица' 'Золотая улица' 'Зоологическая улица' 'Зубарев переулок' 'Ивановская улица' 'Иваньковское шоссе' 'Игарский проезд' 'Измайловская площадь' 'Инициативная улица' 'Казарменный переулок' 'Каланчёвская улица' 'Каргопольская улица' 'Клинская улица' 'Коломенская набережная' 'Колымажный переулок' 'Кольцевая линия' 'Коммунистический переулок' 'Композиторская улица' 'Конюшковская улица' 'Кооперативная улица' 'Коптевский бульвар д 18 А стр 1' 'Костомаровский переулок' 'Костромская улица' 'Котляковская улица' 'Красноармейская улица' 'Красностуденческий проезд' 'Краснохолмская набережная' 'Красноярская улица' 'Крылатский мост' 'Кубанская улица' 'Кузьминская улица' 'Курсовой переулок' 'Кутузовский проезд' 'Лазаревский переулок' 'Ленинский проспект (дублёр)' 'Леонтьевский переулок' 'Лечебная улица' 'Лианозовский парк культуры и отдыха' 'Лианозовский проезд' 'Липецкая улица (дублёр)' 'Мажоров переулок' 'Малая Андроньевская улица' 'Малая Красносельская улица' 'Малая Почтовая улица' 'Малая Тульская улица' 'Маленковская улица' 'Малый Гнездниковский переулок' 'Малый Дровяной переулок' 'Малый Златоустинский переулок' 'Малый Ивановский переулок' 'Малый Казённый переулок' 'Малый Кисловский переулок' 'Малый Николопесковский переулок' 'Малый Патриарший переулок' 'Малый Толмачёвский переулок' 'Мансуровский переулок' 'Медовый переулок' 'Мерзляковский переулок' 'Миусская площадь' 'Михайловский проезд' 'Молдавская улица' 'Москворецкая набережная' 'Московская улица' 'Московский проспект' 'Мурановская улица' 'Мясницкий проезд' 'Нащокинский переулок' 'Новая улица' 'Новгородская улица' 'Новомосковская улица' 'Новопоселковая улица' 'Новороссийская улица' 'Новосущёвская улица' 'Новощукинская улица' 'Новоясеневский тупик' 'Одинцовская улица' 'Октябрьский переулок' 'Ордынский тупик' 'Оренбургская улица' 'Орловский переулок' 'Оршанская улица' 'Островная улица' 'Отрадный проезд' 'Павелецкая набережная' 'Панкратьевский переулок' 'Парусный проезд' 'Певческий переулок' 'Переведеновский переулок' 'Перекопская улица' 'Пермская улица' 'Перовский парк культуры и отдыха' 'Пестовский переулок' 'Петроверигский переулок' 'Петровско-Разумовская аллея' 'Платовская улица' 'Пожарский переулок' 'Полоцкая улица' 'Померанцев переулок' 'Поперечный просек' 'Посланников переулок' 'Прибрежный проезд' 'Проектируемый проезд № 5265' 'Промышленный проезд' 'Пронская улица' 'Просторная улица' 'Проточный переулок' 'Пулковская улица' 'Путевой проезд' 'Пушкарёв переулок' 'Раушская набережная' 'Савёловская линия' 'Самарская улица' 'Самокатная улица' 'Светлогорский проезд' 'Северный бульвар' 'Сеченовский переулок' 'Симферопольский проезд' 'Скатертный переулок' 'Скотопрогонная улица' 'Смоленская-Сенная площадь' 'Советская улица' 'Солянский тупик' 'Софийская набережная' 'Средний Тишинский переулок' 'Ставропольская улица' 'Ставропольский проезд' 'Старокаширское шоссе' 'Староконюшенный переулок' 'Старомонетный переулок' 'Старый Петровско-Разумовский проезд' 'Старый Толмачёвский переулок' 'Стахановская улица' 'Сторожевая улица' 'Стрелецкая улица' 'Стромынский переулок' 'Ступинский проезд' 'Сумская' 'Сумской проезд' 'Сытинский переулок' 'Сытинский тупик' 'Таганский парк культуры и отдыха' 'Талдомская улица' 'Таможенный проезд' 'Тарутинская улица' 'Таёжная улица' 'Терлецкий лесопарк' 'Товарищеский переулок' 'Токмаков переулок' 'Третье транспортное кольцо' 'Триумфальная площадь' 'Троилинский переулок' 'Трёхпрудный переулок' 'Турчанинов переулок' 'Тучковская улица' 'Тюменский проезд' 'Угличская улица' 'Уссурийская улица' 'Федеративный проспект' 'Хитровский переулок' 'Хлыновский тупик' 'Хоромный тупик' 'Хохловский переулок' 'Центральный парк культуры и отдыха имени М. Горького' 'Чапаевский переулок' 'Челобитьевское шоссе' 'Челюскинская улица' 'Чермянский проезд' 'Черёмушкинский проезд' 'Чонгарский бульвар' 'Шоссейный проезд' 'Электрический переулок' 'Юго-Западный административный округ' 'Якиманский переулок' 'Яковоапостольский переулок' 'аллея Молодожёнов' 'ландшафтный заказник Лианозовский' 'парк Алтуфьево' 'парк Ангарские Пруды' 'парк Борисовские пруды' 'парк Братеевская набережная' 'парк Дружбы' 'парк Зюзино' 'парк Красная Пресня' 'парк Левобережный' 'парк Сад будущего' 'парк Технических видов спорта' 'парк Тюфелева роща' 'парк Этнографическая деревня Бибирево' 'парк имени Артёма Боровика' 'парк искусств Музеон' 'переулок Капранова' 'площадь Журавлёва' 'площадь Савёловского Вокзала' 'пр-т Комсомольский' 'природно-исторический парк Измайлово' 'проезд Донелайтиса' 'проезд Одоевского' 'проезд Ольминского' 'проезд Якушкина' 'проспект Академика Сахарова' 'проспект Лихачёва' 'сад Эрмитаж' 'сквер имени М.И. Калинина' 'ул. Профсоюзная' 'ул. Ярославская' 'улица 8 Марта' 'улица 800-летия Москвы' 'улица Айвазовского' 'улица Академика Ильюшина' 'улица Академика Комарова' 'улица Антонова-Овсеенко' 'улица Артюхиной' 'улица Богородский Вал' 'улица Большая Молчановка' 'улица Буженинова' 'улица Бусиновская Горка' 'улица Васильцовский Стан' 'улица Ватутина' 'улица Вешних Вод' 'улица Вильгельма Пика' 'улица Водников' 'улица Всеволода Вишневского' 'улица Вучетича' 'улица Высоцкого' 'улица Габричевского' 'улица Гастелло' 'улица Гашека' 'улица Генерала Глаголева' 'улица Генерала Дорохова' 'улица Генерала Ермолова' 'улица Говорова' 'улица Годовикова' 'улица Грекова' 'улица Губкина' 'улица Демьяна Бедного' 'улица Дудинка' 'улица Зорге' 'улица Ивана Франко' 'улица Ильинка' 'улица Капотня' 'улица Кашёнкин Луг' 'улица Кирпичные Выемки' 'улица Климашкина' 'улица Клочкова' 'улица Комдива Орлова' 'улица Кондратюка' 'улица Константина Федина' 'улица Конёнкова' 'улица Костякова' 'улица Кутузова' 'улица Ленивка' 'улица Лефортовский Вал' 'улица Литвина-Седого' 'улица Лобачика' 'улица Луиджи Лонго' 'улица МЖД Киевское 5-й км' 'улица Максимова' 'улица Малая Полянка' 'улица Малая Якиманка' 'улица Маршала Баграмяна' 'улица Маршала Соколовского' 'улица Мусоргского' 'улица Неверовского' 'улица Николая Химушина' 'улица Олеко Дундича' 'улица Острякова' 'улица Паустовского' 'улица Петра Романова' 'улица Пивченкова' 'улица Поликарпова' 'улица Полины Осипенко' 'улица Раевского' 'улица Расковой' 'улица Расплетина' 'улица Ремизова' 'улица Речников' 'улица Рогова' 'улица Розанова' 'улица Рокотова' 'улица Россолимо' 'улица Садовники' 'улица Саляма Адиля' 'улица Самеда Вургуна' 'улица Саморы Машела' 'улица Седова' 'улица Советской Армии' 'улица Станиславского' 'улица Старые Кузьминки' 'улица Стасовой' 'улица Усиевича' 'улица Уткина' 'улица Фадеева' 'улица Фотиевой' 'улица Чечулина' 'улица Чистова' 'улица Шкулёва' 'улица Шкулёва 4' 'улица Шухова' 'улица Юннатов' '№ 7']
Посмотрим, какие заведения расположены на таких улицах.
# Фильтруем таблицу data, чтобы оставить только улицы, где есть только 1 заведение
sorted_data10 = pd.merge(sorted_data9, data, on='street')
print('Всего', sorted_data10['name'].nunique(), 'заведений являются единственными заведениями на улице.')
print('----')
print('Из них', sorted_data10.query('chain==1')['name'].count(), 'являются сетевыми заведениями.')
print('----')
print('Список заведения, которые являются единственными заведениями на улице:')
print()
print(sorted_data10['name'].unique())
Всего 403 заведений являются единственными заведениями на улице. ---- Из них 133 являются сетевыми заведениями. ---- Список заведения, которые являются единственными заведениями на улице: ['Чайхана Азия' 'Хуан Хэ' 'Колизей' 'Deli by Shell' 'Shelby' 'Домашняя кухня' 'Жаровня-офис' 'Арти' 'Халал' 'Вкус Life' 'Время чудес' 'Вкусное' 'Чердачок' 'Ресторан Бабель' 'Dada' 'Как Дома' 'Кофе пью' 'Сетунь' 'E. D. A.' 'БонАрхитект' 'Ставр' 'Тандыр' 'Две печеньки' 'Кафе' 'Фреско' 'Кофейня 613' 'Сосны' 'Twice' 'Шаурма' 'Тихий дом' 'Сезоны' 'Кафе домашняя еда' "Gump's" 'Le Круассан' 'Сущёвский двор' 'Монастырская трапеза' 'Кафе-столовая' 'Монастырская чайная' 'Пирожковая' 'Кафе Шашлычная 1957' 'Мадина' 'Борго' 'Spoon&Dagger' 'Sun Stek' 'Ресторан Доктор на крыше' 'Кафе-столовая Техника питания' 'Самаргало' 'Пекарня Буханка' 'Яндекс.Лавка' 'Burgers and Crabs' 'Дрова и Угли' 'Farn House' 'Sub Cafe' 'Ресторан корейской кухни Новая Азия' 'Миль' 'Столовая' 'Чайхана Vizir' 'Кофе On' 'Кафе 11/11' 'Грузинская Лавка' 'CofeFest' 'Галерея вкуса' 'Тайм-ланч' 'Гелена' 'Pique-Nique' 'Кулинарная лавка братьев Караваевых' 'Диемм' 'Лаки Сувлаки' 'Prime' 'Магазин кафе' 'Формула за рулем' 'Ногбон осетинские пироги' 'City Lunch' 'Дубки' 'Грузинская кухня' 'Хинкальная у Бехо' 'Пивной бар Pivo. Bar' 'Кафе Пляж' 'Трапезная' 'Столовая в Марьиной роще' 'Single' 'Бахытик' 'Air Coffee' 'Шаурма - Донер' 'Басманный парк' 'Chicken Mania' 'Теремок' 'Жигулевское' 'Ex: Libris' 'Циники' 'Вителия' 'Портал' 'Кафетериус № 26' 'Кондитерская-кулинария Брусника' 'La Veranda' 'Калитки' 'Вкус кофе' 'White Fox' 'Мамина еда' 'Johnny Lee Pizza & Halal Food' 'Noba coffee' 'Буфет ботанического сада' 'Well Doner' 'Сказка' 'Мирабель' 'Ланч Бокс' 'Баам-кафе' 'Академия' 'Чайхана Баракат' 'Bollo' 'Шашлык.рф' 'One Price Coffee' 'Угол' 'Self Cafe' 'Км халяль' 'Prawns' 'Star' 'Хаят' 'Drive Café' 'Эларджи' 'Самса Халяль' 'Чайхана' 'Караван' 'Кофе' 'Городское кафе 317' 'Чайхона №1' 'Мегобребо' 'Encore Cafe' '100ловая' 'Grace pizza' 'Лига Шашлыков' 'Ланч поинт' 'Скрепка' 'Итальянский ресторан DaPino' 'Пикник' 'Кантинетта Антинори' 'Салхино' 'Seven' 'Блеск' 'Осетинские пироги Pir & Rog' 'Бистро' 'Ivoire café' 'Тер. мос' 'Вкус Востока' 'ТЦ' 'Шашлычная' 'Зарайский дворик' 'Profitt' 'Сити Лайф Ривер' 'Vegolife' 'Сторожка' 'First&Only' "Домино'с Пицца" 'Шоколадница' 'Наш двор' 'Эль кафе' 'ВьетПлюс' 'Sova coffee' 'Шаурма на районе № 1' 'Хазар' 'Кафе-трапезная Antiпa' 'Рукав' 'Столовая-буфет' 'Corner cafe&kitchen' 'Zoo Beer & Grill' 'Шантимэль' 'История' 'Алло! Пицца' 'Наша История' 'Naturality' 'Оливка' 'Coffeewinner' 'Langet' 'Пеньки' 'Лайфхакер' 'Беседка' 'Кафе на Курсовом' 'Имбирный карп' 'Культ Вкуса' 'FoodMoscowCoffee' 'Чехонте' 'Мохамед' 'Testo мания' 'Тетри' 'Coffeplet' 'Мажор' 'Шафран' 'Bon Lavash' 'Чайхана Зейтун' 'Ровесник' 'Кофейня на Дровах' 'Гастробар БеловЪ' 'Ноев Ковчег' 'Тинто' 'Dobryakova Bakery' 'Амбарчик' 'From Berlin' 'Приятного аппетита' 'Гастробар Cavina' 'Кофедей' 'Brera' 'Лао Ли Менделеев' 'Глинтвейн' 'Фалафилло' 'Ням по пути' 'Мещерское' 'Пиццаменто' 'Мастер вкуса' 'Барави' 'Александр' 'Аквафортуна' 'Чай хана халал' 'Лагман Хаус' 'Ганга' 'Буфет' 'Кампа' 'ABC Coffee Roasters' 'Шаурамус' 'КОФЕПОРТ' 'Kaya Coffee Shop' 'Пармижано' 'St Cultura' 'Дружба' 'Легенда Самарканда' 'Экспедиция. Северная кухня' 'Bonne Cantine' 'Оля' 'Трдельникъ' 'Tasty Joys' 'Il Letterato' 'Итальянские каникулы' 'Фергана' '15 Kitchen+Bar' 'Пельменная' 'Korean Chick' '7/12' 'Линии вкуса' 'Арт-кафе Сахар' 'Пекорино' 'Мазза' 'Бар, паб Проточный' 'Mr. Brown' 'Кафе Пушкарёв' 'Санапиро' 'Чайхана Халаль 24' 'Fire Lake' '1901 Comfort Food Zone' 'Иду лесом' 'Энесай' 'Багратион' 'Кухня & Маркет' 'BBCafe' 'Де Марко' 'Ресторан' 'Люди как люди' 'Сакура Place' 'Актавест' 'Звездопад' 'Брусника' 'Baci Abbracci' 'Мустафа Кебаб' 'Антилопа' 'Роза Ветров' 'ЧАЙХАНА 24/7' '9 Bar Coffee' 'Достонжон' 'Eco шаурма' 'Кадастровая палата' 'Bowlroom' 'Tilda Food & Bar' 'АндерСон' 'Шах-Даг' 'Ланч и Кофе' 'Мастера Тарутины' 'Светлана' 'Method Beer & Munchies' 'Кафе Токмакоff' 'Кафе Чайковский' 'Му-Му' 'Рыбный базар' 'Brasserie Lambic' 'Таверна Фили Хаус' 'Смакбери' "Coffeekaldi's" 'Шаверма' 'Восточная кухня' 'Сыр и кофе' 'Квартира 44' 'Blanc' 'Пион' 'Сокол' 'Алазани' 'Guru' 'Антик' 'Мое кафе' 'Практика Кофе' 'Удача' 'Семь хлебов' 'Drink Eat Travel' 'Кофепровод' 'В самое сердце' 'Чебуречная история' 'Веранда' 'В парке вкуснее' 'Cafe Vecher' 'Фастфуд' 'Пекарня' 'Cafe Racer' 'Di villaggio' 'Кафе-Кофе' 'Пончики' 'Четвёртая стена' 'ZAcoffee' 'Чёрный кот' 'French Bakery SeDelice' 'ФО БО ю' 'Сеть кофеен Латте' 'Донер Тандыр' 'Хорошие знакомые' 'Фудмаркет' 'Бургер Бокс' "G'астроном" 'Coffeepie' "Jeffrey's Coffee" 'Сеть поминальных залов' 'Услада' 'Арамье' 'Стильавто' 'Любить Кофе Пить' 'Ля Фантази' 'Чайхона' 'WTFcoffee' 'Завтрак на обед' 'Траттория Evi' 'Дом обеда' 'Torsher' 'Марина' 'Кафетерий' 'Съестная лавка' 'Шале-Прайд' 'Донер и гриль' 'Клуб Высоцкий' 'E. S. Coffee' 'Альпини' 'Самолёт' 'Хинкальная' 'Кафе Москва' 'Кофе и' 'Папа Карло' 'Хлеб с маслом' 'Шале-микс' 'Азербайджан' 'Coffee Itself' 'Кафе Арбат' 'Тейст' 'Саят-Нова' 'Eat&Play' 'Чайная' 'Марио' 'Магнолия' 'Cosmic Coffee' 'Mr. Пи' '1 Этаж' 'Фитнес кафе fresh fit' 'Пельмени' 'Телиани' 'Хинкальный дворик' 'Палладиум' 'Гранат Black' 'Империя' 'Онегинъ' 'William Bass' 'Ресторан Cafe de Arts' 'Green V. A. I.' 'Ресторан-бар Crazy Brothers' 'Три жирафа' '10 Идеальных Пицц' 'Кофейня Анклав' 'Plov Time' 'Буханка' 'Coffee Point' 'БирХеванс ресторан бар' 'Остерия итальянцы' 'Речниковъ' 'По кайфу' 'Coffeebrain' 'Кафе-гриль' 'Луар' 'Food Story' 'Кап кап' 'Культура Вкусов' 'Бургер & Кофе' 'Лоза' 'Чебуреки' 'Ирина' 'Папа Джонс' 'Гараж Гриль & Кофе Тайм' 'Просвет' 'Baza' 'Палермо, кафе' 'Мираж' 'Scirocco' 'Енот']
Вывод
Для каждого округа посчитаем медианное значение стоимости заказа в заведениях, которые находятся на его территории.
# Фильтруем только те строки, где у нас есть числовые значения для заказа
bill_data = data.query('middle_avg_bill != "N/A"')
# Переводим в числовой формат
bill_data['middle_avg_bill'] = pd.to_numeric(bill_data['middle_avg_bill'])
# Готовим данные для отражения на карте
bill_data = bill_data.groupby('district', as_index=False)['middle_avg_bill'].agg('median')\
.sort_values(by='middle_avg_bill', ascending=False).reset_index().drop('index', axis=1)
bill_data
| district | middle_avg_bill | |
|---|---|---|
| 0 | Западный административный округ | 1000.0 |
| 1 | Центральный административный округ | 1000.0 |
| 2 | Северо-Западный административный округ | 700.0 |
| 3 | Северный административный округ | 650.0 |
| 4 | Юго-Западный административный округ | 600.0 |
| 5 | Восточный административный округ | 575.0 |
| 6 | Северо-Восточный административный округ | 500.0 |
| 7 | Южный административный округ | 500.0 |
| 8 | Юго-Восточный административный округ | 450.0 |
Изобразим полученные значения на карте Москвы, построим фоновую картограмму.
# создаём карту Москвы
m2 = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=bill_data,
columns=['district', 'middle_avg_bill'],
key_on='feature.name',
fill_color='PuBu',
fill_opacity=0.8,
legend_name='Медианная стоимость заказа',
).add_to(m2)
# выводим карту
m2
Вывод
Основателям фонда «Shut Up and Take My Money» не даёт покоя успех сериала «Друзья». Их мечта — открыть такую же крутую и доступную, как «Central Perk», кофейню в Москве. Заказчики не боятся конкуренции в этой сфере, ведь кофеен в больших городах уже достаточно. Попробуем проанализировать, осуществима ли эта мечта.
Кофейню потенциально можно расположить в двух местах: 1) на улице, где отсутствуют другие кофейни, и конкуренция
2) на улице, где существует большая концентрация кофеен, и, соответственно,потребители специально туда едут за кофе из-за наличия большого выбора (при этом надо учитывать, чтобы на этой улице отсутствовали сетевые кофейни, так как их потребители будут лояльны бренду)
Для начала посмотрим, как количество кофеен меняется в зависимости от района.
# Подготовим данные для графика
sorted_data11 = sorted_data6.query('category=="кофейня"').sort_values(by='count_cat', ascending=False)
# Создание столбчатой диаграммы
fig11 = go.Figure()
# Добавление столбцов
fig11.add_trace(go.Bar(x=sorted_data11['distr_short'][:-1], y=sorted_data11['count_cat'][:-1],
marker=dict(color='rgba(0, 0, 255, 1)'), # Можете выбрать свой цвет для столбцов
hovertemplate='<br>'.join([
'Административный район: %{x}',
'Количество кофеен: %{y}'
])
))
fig11.add_trace(go.Bar(x=sorted_data11['distr_short'][-1:], y=sorted_data11['count_cat'][-1:],
marker=dict(color='rgba(255, 0, 0, 1.0)'), # Можете выбрать свой цвет для последнего столбца
hovertemplate='<br>'.join([
'Административный район: %{x}',
'Количество кофеен: %{y}'
])
))
# Настройка внешнего вида диаграммы
fig11.update_layout(
title={
'text': 'Количество кофеен в районах Москвы',
'font': {'size': 20}
},
xaxis_title={
'text': 'Административный район',
'font': {'size': 16}
},
yaxis_title={
'text': 'Количество кофеен',
'font': {'size': 16}
},
font=dict(
size=15
), showlegend=False)
# Установка размера графика
fig11.update_layout(width=950, height=600)
fig11.show()
Вывод
data_streets_0¶Выделим те улицы СЗАО, где отсутствуют другие кофейни, и конкуренция.
# группируем данные по улицам и категориям, считаем количество заведений
sorted_data12 = data.groupby(['street', 'category']).agg({'address': 'count'}).reset_index()
# фильтруем улицы, на которых нет категории "кофейня"
sorted_data12 = sorted_data12.groupby('street').filter(lambda x: 'кофейня' not in x['category'].values)['street'].unique()
# создаем таблицу с информацией об улицах СЗАО , где отсутствует кофейня
data_streets_0 = data[data['street'].isin(sorted_data12)].reset_index().drop('index', axis=1)
data_streets_0 = data_streets_0.query('distr_short=="СЗАО"').reset_index().drop('index', axis=1)
data_streets_0['street'].unique()
array(['Парусный проезд', 'Походный проезд', 'Лодочная улица',
'улица Мещерякова', 'бульвар Яна Райниса', 'улица Фомичёвой',
'улица Фабрициуса', 'улица Василия Петушкова',
'улица Вилиса Лациса', 'Строительный проезд', 'проезд Донелайтиса',
'Новопоселковая улица', 'Таллинская улица', 'улица Твардовского',
'Одинцовская улица', 'улица Исаковского',
'памятник природы Серебряный бор',
'4-я линия Хорошёвского Серебряного Бора', 'улица Маршала Рыбалко',
'улица Максимова', 'улица Маршала Соколовского',
'улица Маршала Тухачевского', 'улица Расплетина',
'улица Академика Бочвара', 'Живописная улица',
'Иваньковское шоссе', '1-я линия Хорошёвского Серебряного Бора',
'Большой Волоколамский проезд', 'улица Паршина',
'Новохорошёвский проезд', 'улица Маршала Новикова',
'улица Генерала Глаголева', 'улица Водников', 'улица Рогова',
'улица Демьяна Бедного', 'улица Саляма Адиля',
'1-й Силикатный проезд', 'Причальный проезд',
'2-й Силикатный проезд'], dtype=object)
data_streets_1¶# фильтруем данные и группуируем по улице
sorted_data18 = data.query('category=="кофейня" and distr_short=="СЗАО" and chain==0')\
.groupby(['street']).agg({'address': 'count'}).reset_index()
# оставляем названия улицы СЗАО с большой концентрацией несетевых кофеен (2 и более)
sorted_data18 = sorted_data18.query('address>=2').reset_index().drop('index', axis=1)['street'].unique()
# создаем новый датафрейм со всеми данными из data, где будут улицы СЗАО с большой концентрацией несетевых кофеен
data_streets_1 = data[data['street'].isin(sorted_data18)].reset_index().drop('index', axis=1)
data_streets_1 = data_streets_1.query('category=="кофейня" and chain==0').reset_index().drop('index', axis=1)
print('улицы СЗАО с большой концентрацией несетевых кофеен (2 и более):')
print()
print(data_streets_1['street'].unique())
улицы СЗАО с большой концентрацией несетевых кофеен (2 и более): ['Сходненская улица' 'улица Маршала Катукова' 'бульвар Генерала Карбышева' 'Шелепихинская набережная']
# Подготовим данные для графика
sorted_data17 = data_streets_1.groupby(['street']).agg({'address': 'count'}).reset_index()
# Отсортируем данные
sorted_data17 = sorted_data17.sort_values(by=['address'], ascending=True)
# Создание столбчатой диаграммы
fig17 = px.bar(sorted_data17, y='street', x='address', labels={
'street': 'Улица',
'address': 'Количество кофеен'
})
# Настройка внешнего вида диаграммы
fig17.update_layout(title={
'text': 'Улицы СЗАО с большой концентрацией несетевых кофеен',
'font': {'size': 20}
},
xaxis_title={
'text': 'Количество кофеен',
'font': {'size': 16}
},
yaxis_title={
'text': 'Улица',
'font': {'size': 16}
})
fig17.update_xaxes(tickfont=dict(size=14), dtick=1)
fig17.update_yaxes(tickfont=dict(size=14))
fig17.show()
Вывод
Мы выделили улицы СЗАО, где лучше расположить кофейню. Проанализируем другие критерии, чтобы сузить выборку улиц.
Проанализируем, много ли в Москве круглосуточных кофеен.
# готовим данные для графика
sorted_data13 = data.query('category=="кофейня"')['is_24_7'].value_counts().sort_values(ascending=False).reset_index()
sorted_data13.columns = ['is_24_7', 'count']
sorted_data13
| is_24_7 | count | |
|---|---|---|
| 0 | False | 1354 |
| 1 | True | 59 |
# готовим данные для графика - переименуем булевые значения
sorted_data13['is_24_7'] = sorted_data13['is_24_7'].replace([False, True], ['не круглосуточные', 'круглосуточные'])
# строим круговую диаграмму
fig13 = go.Figure(data=[go.Pie(labels=sorted_data13['is_24_7'], # указываем значения, которые появятся на метках сегментов
values=sorted_data13['count'], # указываем данные, которые отобразятся на графике
pull = [0.1, 0])]) # добавляем аргумент, который выделит тип-лидер на графике
fig13.update_layout(title='Соотношение круглосуточных и не круглосуточных кофеен', # указываем заголовок графика
width=830, # указываем размеры графика
height=500,
annotations=[dict(x=1.12, # вручную настраиваем аннотацию легенды
y=1.05,
text='Количество кофеен',
showarrow=False)],
font=dict(size=13))
fig13.show() # выводим график
Вывод
в Москве доля круглосуточных кофеен крайне низкая - 4.18%; скорее всего, это означает, что при прочих равных держать круглосуточную кофейню не выгодно (круглосуточными лучше делать бары/пабы)
делать круглосуточные кафе имеет смысл только чтобы заработать конкурентное преимущество в местах с большой концентрацией кофеен.
data_streets_1 на наличие круглосуточных кофеен¶data_streets_1.query('is_24_7==True')
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | is_24_7 | distr_short |
|---|
Вывод
Посмотрим, как рейтинг кофеен меняются в зависимости от района.
# Определяем порядок сортировки
sorted_data14 = data.query('category=="кофейня"').groupby('distr_short').agg({'rating': 'median'}).sort_values('rating', ascending=False).reset_index()
# Задаем размер графика
plt.figure(figsize=(8, 6))
# Создаем график
graph14 = sns.barplot(x='distr_short', y='rating', data=sorted_data14)
# Поворачиваем подписи на 45 градусов
labels14 = graph14.get_xticklabels()
graph14.set_xticklabels(labels14, rotation=45)
# Добавляем значения над каждым столбцом
for index, row in sorted_data14.iterrows():
graph14.annotate('{:.1f}'.format(row['rating']), xy=(index, row['rating']), ha='center', va='bottom')
# ограничиваем ось Y для наглядности
plt.ylim(4, 4.4)
# Добавляем заголовок и подписи осей
plt.title('Медианные рейтинги кофеен')
plt.xlabel('Административные районы')
plt.ylabel('Медианный рейтинг')
# Отображаем график
plt.show()
Вывод
data_streets_1 (улицы с большой концентрацией несетевых кофеен)¶# определяем порядок сортировки
sorted_data15 = data_streets_1.groupby('street').agg({'rating': 'median'}).sort_values('rating', ascending=False).reset_index()
sorted_data15['rating'] = sorted_data15['rating'].apply(lambda x: round(x, 2))
# строим столбчатую диаграмму
fig15 = px.bar(sorted_data15.sort_values(by='rating', ascending=True), # загружаем данные и заново их сортируем
x='rating', # указываем столбец с данными для оси X
y='street', # указываем столбец с данными для оси Y
text='rating', # добавляем аргумент, который отобразит текст с информацией о количестве
hover_data=['rating'] # добавляем аргумент, который отобразит категорию при наведении курсора
)
# оформляем график
fig15.update_layout(title='Рейтинги кофеен на улицах с большой концентрацией несетевых кофеен',
xaxis_title='Медианный рейтинг',
yaxis_title='Улица',
font=dict(size=13)) # увеличиваем размер шрифта
# красим нижний столбец в красный
fig15.update_traces(marker=dict(color=['red' if x == sorted_data15['rating'].max() else 'blue' for x in sorted_data15['rating']]))
fig15.update_xaxes(range=[3.6, 4.8])
fig15.show() # выводим график
Вывод
На улице Маршала Катукова медианный пользовательский рейтинг кофеен самый низкий среди ТОП-4 улиц СЗАО по концентрации несетевых кофеен. На этой улице есть возможность открыть кафе и навязать конкуренцию.
data_streets_0 (улицы, где отсутствуют кофейни)¶Так как мы до этого выявили много улиц СЗАО, где отсутствуют кофейни, сразу поставим фильтр на средний рейтинг заведений меньше 4 и покажем оставшиеся улицы на графике.
# определяем порядок сортировки
sorted_data16 = data_streets_0.groupby('street').agg({'rating': 'median'}).query('rating<4').sort_values('rating', ascending=False).reset_index()
# ручная корректировка самого крупного значения, чтобы максимум был только один (нужно, чтобы удалось закрасить только один элемент)
sorted_data16.loc[0, 'rating'] = sorted_data16.loc[0, 'rating']+0.01
sorted_data16['rating'] = sorted_data16['rating'].apply(lambda x: round(x, 2))
# строим столбчатую диаграмму
fig16 = px.bar(sorted_data16.sort_values(by='rating', ascending=True), # загружаем данные и заново их сортируем
x='rating', # указываем столбец с данными для оси X
y='street', # указываем столбец с данными для оси Y
text='rating', # добавляем аргумент, который отобразит текст с информацией о количестве
hover_data=['rating'] # добавляем аргумент, который отобразит категорию при наведении курсора
)
# оформляем график
fig16.update_layout(title='Медианные рейтинги заведений на улицах без кофеен',
xaxis_title='Медианный рейтинг',
yaxis_title='Улица',
font=dict(size=13)) # увеличиваем размер шрифта
# красим нижний столбец в красный
fig16.update_traces(marker=dict(color=['red' if x == sorted_data16['rating'].max() else 'blue' for x in sorted_data16['rating']]))
fig16.update_xaxes(range=[2, 4])
fig16.show() # выводим график
Вывод
В Новохорошевском проезде отсутствуют кофейни, а заведения общепита, которые там есть, получили низкий рейтинг от посетителей. Следовательно, кофейню лучше открыть там.
Проанализируем медианную стоимость чашки капучино в Москве: для каждого округа посчитаем медианное значение стоимости капучино в кофейнях, которые находятся на его территории.
# Фильтруем только те строки, где у нас есть числовые значения для заказа
cof_data = data.query('middle_coffee_cup != "N/A" and category=="кофейня"')
# Переводим в числовой формат
cof_data['middle_coffee_cup'] = pd.to_numeric(cof_data['middle_coffee_cup'])
# Готовим данные для отражения на карте
cof_data = cof_data.groupby('district', as_index=False)['middle_coffee_cup'].agg('median')\
.sort_values(by='middle_coffee_cup', ascending=False).reset_index().drop('index', axis=1)
cof_data
| district | middle_coffee_cup | |
|---|---|---|
| 0 | Юго-Западный административный округ | 198.0 |
| 1 | Центральный административный округ | 190.0 |
| 2 | Западный административный округ | 189.0 |
| 3 | Северо-Западный административный округ | 165.0 |
| 4 | Северо-Восточный административный округ | 162.5 |
| 5 | Северный административный округ | 159.0 |
| 6 | Южный административный округ | 150.0 |
| 7 | Юго-Восточный административный округ | 147.5 |
| 8 | Восточный административный округ | 135.0 |
Изобразим полученные значения на карте Москвы, построим фоновую картограмму.
# создаём карту Москвы
m3 = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=cof_data,
columns=['district', 'middle_coffee_cup'],
key_on='feature.name',
fill_color='Oranges',
fill_opacity=0.8,
legend_name='Медианная стоимость капучино'
).add_to(m3)
# выводим карту
m3
Вывод
Цена на капучино заметно отличается в зависимости от района Москвы. Так как кофейня будет расположена в СЗАО, при открытии стоит ориентироваться на стоимость равную 165 руб.
Рекомендуется открыть кофейню в СЗАО на Новохорошевском проезде или на улице Маршала Катукова.
При открытии кофейни стоит ориентироваться на медианную стоимость капучино в СЗАО - 165 руб.
Презентация: https://disk.yandex.ru/i/dJzZAFEZeNBPHw